#include "globals.h"
#include "errno.h"

#include "util/string.h"
#include "util/debug.h"

#include "mm/mmobj.h"
#include "mm/pframe.h"
#include "mm/mm.h"
#include "mm/page.h"
#include "mm/slab.h"
#include "mm/tlb.h"


int anon_count = 0; /* for debugging/verification purposes */

static slab_allocator_t *anon_allocator;

static void anon_ref(mmobj_t *o);
static void anon_put(mmobj_t *o);
static int  anon_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf);
static int  anon_fillpage(mmobj_t *o, pframe_t *pf);
static int  anon_dirtypage(mmobj_t *o, pframe_t *pf);
static int  anon_cleanpage(mmobj_t *o, pframe_t *pf);

static mmobj_ops_t anon_mmobj_ops = {
        .ref = anon_ref,
        .put = anon_put,
        .lookuppage = anon_lookuppage,
        .fillpage  = anon_fillpage,
        .dirtypage = anon_dirtypage,
        .cleanpage = anon_cleanpage
};

/*
 * This function is called at boot time to initialize the
 * anonymous page sub system. Currently it only initializes the
 * anon_allocator object.
 */
void
anon_init()
{
	anon_allocator=slab_allocator_create("anon",sizeof(mmobj_t));
	KASSERT(anon_allocator);

	dbg(DBG_VM,"(GRADING 2.a)annon_allocator is not NULL\n");
        /*NOT_YET_IMPLEMENTED("VM: anon_init");*/
}

/*
 * You'll want to use the anon_allocator to allocate the mmobj to
 * return, then then initialize it. Take a look in mm/mmobj.h for
 * macros which can be of use here. Make sure your initial
 * reference count is correct.
 */
mmobj_t *
anon_create()
{
	dbg(DBG_ELF, "Enter anon_create\n");
        mmobj_t *anon_mmobj;
	anon_mmobj=(mmobj_t*)slab_obj_alloc(anon_allocator);
	mmobj_init(anon_mmobj, &anon_mmobj_ops);
	anon_mmobj->mmo_refcount=1;
	return anon_mmobj;
        /*NOT_YET_IMPLEMENTED("VM: anon_create");
        return NULL;*/
}

/* Implementation of mmobj entry points: */

/*
 * Increment the reference count on the object.
 */
static void
anon_ref(mmobj_t *o)
{
	KASSERT(o && (0 < o->mmo_refcount) && (&anon_mmobj_ops == o->mmo_ops));
    dbg(DBG_VM,"(GRADING 2.b)o->mmo_ops = anon_mmobj_ops and o->mmo_refcount > 0\n");
	o->mmo_refcount++;
        /*NOT_YET_IMPLEMENTED("VM: anon_ref");*/
}

/*
 * Decrement the reference count on the object. If, however, the
 * reference count on the object reaches the number of resident
 * pages of the object, we can conclude that the object is no
 * longer in use and, since it is an anonymous object, it will
 * never be used again. You should unpin and uncache all of the
 * object's pages and then free the object itself.
 */
static void
anon_put(mmobj_t *o)
{
	pframe_t *anon_pframe;
	KASSERT(o && (0 < o->mmo_refcount) && (&anon_mmobj_ops == o->mmo_ops));
	dbg(DBG_VM,"(GRADING 2.c)o->mmo_ops = anon_mmobj_ops and o->mmo_refcount > 0\n");

	if(o->mmo_refcount>o->mmo_nrespages)
		o->mmo_refcount--;
	else
		return;
	if(o->mmo_refcount==o->mmo_nrespages)
	{
		list_iterate_begin(&o->mmo_respages, anon_pframe, pframe_t, pf_olink)
		{
			while(anon_pframe->pf_pincount>0)
				pframe_unpin(anon_pframe);
			pframe_free(anon_pframe);
		}list_iterate_end();
		

		slab_obj_free(anon_allocator,o);   
	}
	
        /*NOT_YET_IMPLEMENTED("VM: anon_put");*/
}

/* Get the corresponding page from the mmobj. No special handling is
 * required. */
static int
anon_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf)
{
	dbg(DBG_ELF, "Enter anon_lookuppage pagenum %d\n",pagenum);
	pframe_t *anon_pframe;
		list_iterate_begin(&o->mmo_respages, anon_pframe, pframe_t, pf_olink)
		{
			
			if(anon_pframe-> pf_pagenum==pagenum)
			{
				*pf=anon_pframe;
				return 0;
			
			}
		}list_iterate_end();
	
	dbg(DBG_ELF, "Leave anon_lookuppage with error\n");
        /*NOT_YET_IMPLEMENTED("VM: anon_lookuppage");*/
        return -1;
}

/* The following three functions should not be difficult. */

static int
anon_fillpage(mmobj_t *o, pframe_t *pf)
{
	dbg(DBG_ELF, "Enter anon_fillpage\n");
       KASSERT(pframe_is_busy(pf));
       dbg(DBG_VM,"(GRADING 2.d)pf is not busy.\n");
       KASSERT(!pframe_is_pinned(pf));
       dbg(DBG_VM,"(GRADING 2.d)pf is not pinned.\n");
       pframe_t *anon_pframe;
	int ret;
	memset(pf->pf_addr,0,PAGE_SIZE);
       /* NOT_YET_IMPLEMENTED("VM: anon_fillpage");*/
        return 0;
}

static int
anon_dirtypage(mmobj_t *o, pframe_t *pf)
{
	/*int ret;	
	if(ret = pframe_get(o, pf->pf_pagenum, &pf)<0)
		return ret;*/
	
	return 0;
        /*NOT_YET_IMPLEMENTED("VM: anon_dirtypage");
        return -1;*/
}

static int
anon_cleanpage(mmobj_t *o, pframe_t *pf)
{
	/*pframe_t *anon_pframe;
	int ret;
	if((ret=anon_lookuppage(o, pf->pf_pagenum,1, &anon_pframe))<0)
		return ret;
	memcpy(PN_TO_ADDR(anon_pframe->pf_pagenum),(void*)((uintptr_t)pf->pf_addr + PAGE_OFFSET(pf->pf_addr)),PAGE_SIZE);*/
	return 0;
        /*NOT_YET_IMPLEMENTED("VM: anon_cleanpage");
        return -1;*/
}
